﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Modeling;
using System.Xml;
using System.Text.RegularExpressions;

namespace Microsoft.Samples.DslTools.ConfigurationSectionDesigner
{
    public partial class ConfigurationPropertySerializer
    {
        /// <summary>
        /// Custom deserialization of ConfigurationProperty's attributes. This is required because we're not
        /// storing the CustomAttributes as an attribute.
        /// </summary>
        private void CustomReadPropertiesFromAttributes( SerializationContext serializationContext, ModelElement element, XmlReader reader )
        {
            ConfigurationProperty instanceOfConfigurationProperty = element as ConfigurationProperty;
            global::System.Diagnostics.Debug.Assert( instanceOfConfigurationProperty != null, "Expecting an instance of ConfigurationProperty" );

            // Name
            if( !serializationContext.Result.Failed )
            {
                string attribName = reader.GetAttribute( "name" );
                if( attribName != null )
                {
                    global::System.String valueOfName;
                    if( SerializationUtilities.TryGetValue<global::System.String>( SerializationUtilities.UnescapeXmlString( attribName ), out valueOfName ) )
                    {
                        instanceOfConfigurationProperty.Name = valueOfName;
                    }
                    else
                    {	// Invalid property value, ignored.
                        ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.IgnoredPropertyValue( serializationContext, reader, "name", typeof( global::System.String ), attribName );
                    }
                }
            }
            // IsRequired
            if( !serializationContext.Result.Failed )
            {
                string attribIsRequired = reader.GetAttribute( "isRequired" );
                if( attribIsRequired != null )
                {
                    global::System.Boolean valueOfIsRequired;
                    if( SerializationUtilities.TryGetValue<global::System.Boolean>( SerializationUtilities.UnescapeXmlString( attribIsRequired ), out valueOfIsRequired ) )
                    {
                        instanceOfConfigurationProperty.IsRequired = valueOfIsRequired;
                    }
                    else
                    {	// Invalid property value, ignored.
                        ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.IgnoredPropertyValue( serializationContext, reader, "isRequired", typeof( global::System.Boolean ), attribIsRequired );
                    }
                }
            }
            // IsKey
            if( !serializationContext.Result.Failed )
            {
                string attribIsKey = reader.GetAttribute( "isKey" );
                if( attribIsKey != null )
                {
                    global::System.Boolean valueOfIsKey;
                    if( SerializationUtilities.TryGetValue<global::System.Boolean>( SerializationUtilities.UnescapeXmlString( attribIsKey ), out valueOfIsKey ) )
                    {
                        instanceOfConfigurationProperty.IsKey = valueOfIsKey;
                    }
                    else
                    {	// Invalid property value, ignored.
                        ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.IgnoredPropertyValue( serializationContext, reader, "isKey", typeof( global::System.Boolean ), attribIsKey );
                    }
                }
            }
            // IsDefaultCollection
            if( !serializationContext.Result.Failed )
            {
                string attribIsDefaultCollection = reader.GetAttribute( "isDefaultCollection" );
                if( attribIsDefaultCollection != null )
                {
                    global::System.Boolean valueOfIsDefaultCollection;
                    if( SerializationUtilities.TryGetValue<global::System.Boolean>( SerializationUtilities.UnescapeXmlString( attribIsDefaultCollection ), out valueOfIsDefaultCollection ) )
                    {
                        instanceOfConfigurationProperty.IsDefaultCollection = valueOfIsDefaultCollection;
                    }
                    else
                    {	// Invalid property value, ignored.
                        ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.IgnoredPropertyValue( serializationContext, reader, "isDefaultCollection", typeof( global::System.Boolean ), attribIsDefaultCollection );
                    }
                }
            }
            // XmlName
            if( !serializationContext.Result.Failed )
            {
                string attribXmlName = reader.GetAttribute( "xmlName" );
                if( attribXmlName != null )
                {
                    global::System.String valueOfXmlName;
                    if( SerializationUtilities.TryGetValue<global::System.String>( SerializationUtilities.UnescapeXmlString( attribXmlName ), out valueOfXmlName ) )
                    {
                        instanceOfConfigurationProperty.XmlName = valueOfXmlName;
                    }
                    else
                    {	// Invalid property value, ignored.
                        ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.IgnoredPropertyValue( serializationContext, reader, "xmlName", typeof( global::System.String ), attribXmlName );
                    }
                }
            }
            // IsReadOnly
            if( !serializationContext.Result.Failed )
            {
                string attribIsReadOnly = reader.GetAttribute( "isReadOnly" );
                if( attribIsReadOnly != null )
                {
                    global::System.Boolean valueOfIsReadOnly;
                    if( SerializationUtilities.TryGetValue<global::System.Boolean>( SerializationUtilities.UnescapeXmlString( attribIsReadOnly ), out valueOfIsReadOnly ) )
                    {
                        instanceOfConfigurationProperty.IsReadOnly = valueOfIsReadOnly;
                    }
                    else
                    {	// Invalid property value, ignored.
                        ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.IgnoredPropertyValue( serializationContext, reader, "isReadOnly", typeof( global::System.Boolean ), attribIsReadOnly );
                    }
                }
            }
            // Documentation
            if( !serializationContext.Result.Failed )
            {
                string attribDocumentation = reader.GetAttribute( "documentation" );
                if( attribDocumentation != null )
                {
                    global::System.String valueOfDocumentation;
                    if( SerializationUtilities.TryGetValue<global::System.String>( SerializationUtilities.UnescapeXmlString( attribDocumentation ), out valueOfDocumentation ) )
                    {
                        instanceOfConfigurationProperty.Documentation = valueOfDocumentation;
                    }
                    else
                    {	// Invalid property value, ignored.
                        ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.IgnoredPropertyValue( serializationContext, reader, "documentation", typeof( global::System.String ), attribDocumentation );
                    }
                }
            }
            // CustomAttributes
            /*if( !serializationContext.Result.Failed )
            {
                string attribCustomAttributes = reader.GetAttribute( "customAttributes" );
                if( attribCustomAttributes != null )
                {
                    string serializedAttributes = SerializationUtilities.UnescapeXmlString( attribCustomAttributes );
                    AttributeList valueOfCustomAttributes = (AttributeList)serializedAttributes;

                    if (valueOfCustomAttributes != null)
                    {
                        instanceOfConfigurationProperty.CustomAttributes = valueOfCustomAttributes;
                    }
                    else
                    {	// Invalid property value, ignored.
                        ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.IgnoredPropertyValue( serializationContext, reader, "customAttributes", typeof( AttributeList ), attribCustomAttributes );
                    }
                }
            }*/
            // Browsable
            if( !serializationContext.Result.Failed )
            {
                string attribBrowsable = reader.GetAttribute( "browsable" );
                if( attribBrowsable != null )
                {
                    global::System.Boolean valueOfBrowsable;
                    if( SerializationUtilities.TryGetValue<global::System.Boolean>( SerializationUtilities.UnescapeXmlString( attribBrowsable ), out valueOfBrowsable ) )
                    {
                        instanceOfConfigurationProperty.Browsable = valueOfBrowsable;
                    }
                    else
                    {	// Invalid property value, ignored.
                        ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.IgnoredPropertyValue( serializationContext, reader, "browsable", typeof( global::System.Boolean ), attribBrowsable );
                    }
                }
            }
            // DisplayName
            if( !serializationContext.Result.Failed )
            {
                string attribDisplayName = reader.GetAttribute( "displayName" );
                if( attribDisplayName != null )
                {
                    global::System.String valueOfDisplayName;
                    if( SerializationUtilities.TryGetValue<global::System.String>( SerializationUtilities.UnescapeXmlString( attribDisplayName ), out valueOfDisplayName ) )
                    {
                        instanceOfConfigurationProperty.DisplayName = valueOfDisplayName;
                    }
                    else
                    {	// Invalid property value, ignored.
                        ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.IgnoredPropertyValue( serializationContext, reader, "displayName", typeof( global::System.String ), attribDisplayName );
                    }
                }
            }
            // Category
            if( !serializationContext.Result.Failed )
            {
                string attribCategory = reader.GetAttribute( "category" );
                if( attribCategory != null )
                {
                    global::System.String valueOfCategory;
                    if( SerializationUtilities.TryGetValue<global::System.String>( SerializationUtilities.UnescapeXmlString( attribCategory ), out valueOfCategory ) )
                    {
                        instanceOfConfigurationProperty.Category = valueOfCategory;
                    }
                    else
                    {	// Invalid property value, ignored.
                        ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.IgnoredPropertyValue( serializationContext, reader, "category", typeof( global::System.String ), attribCategory );
                    }
                }
            }
        }

        /// <summary>
        /// Custom serialization of ConfigurationProperty's attributes. This is required because we're not
        /// storing the CustomAttributes as an attribute.
        /// </summary>
        private void CustomWritePropertiesAsAttributes( SerializationContext serializationContext, ModelElement element, XmlWriter writer )
        {
            ConfigurationProperty instanceOfConfigurationProperty = element as ConfigurationProperty;
            global::System.Diagnostics.Debug.Assert( instanceOfConfigurationProperty != null, "Expecting an instance of ConfigurationProperty" );

            // Name
            if( !serializationContext.Result.Failed )
            {
                global::System.String propValue = instanceOfConfigurationProperty.Name;
                if( !serializationContext.Result.Failed )
                {
                    writer.WriteAttributeString( "name", propValue );
                }
            }
            // IsRequired
            if( !serializationContext.Result.Failed )
            {
                global::System.Boolean propValue = instanceOfConfigurationProperty.IsRequired;
                string serializedPropValue = SerializationUtilities.GetString<global::System.Boolean>( serializationContext, propValue );
                if( !serializationContext.Result.Failed )
                {
                    writer.WriteAttributeString( "isRequired", serializedPropValue );
                }
            }
            // IsKey
            if( !serializationContext.Result.Failed )
            {
                global::System.Boolean propValue = instanceOfConfigurationProperty.IsKey;
                string serializedPropValue = SerializationUtilities.GetString<global::System.Boolean>( serializationContext, propValue );
                if( !serializationContext.Result.Failed )
                {
                    if( serializationContext.WriteOptionalPropertiesWithDefaultValue || string.CompareOrdinal( serializedPropValue, "False" ) != 0 )
                    {	// No need to write the value out if it's the same as default value.
                        writer.WriteAttributeString( "isKey", serializedPropValue );
                    }
                }
            }
            // IsDefaultCollection
            if( !serializationContext.Result.Failed )
            {
                global::System.Boolean propValue = instanceOfConfigurationProperty.IsDefaultCollection;
                string serializedPropValue = SerializationUtilities.GetString<global::System.Boolean>( serializationContext, propValue );
                if( !serializationContext.Result.Failed )
                {
                    writer.WriteAttributeString( "isDefaultCollection", serializedPropValue );
                }
            }
            // XmlName
            if( !serializationContext.Result.Failed )
            {
                global::System.String propValue = instanceOfConfigurationProperty.XmlName;
                if( !serializationContext.Result.Failed )
                {
                    if( !string.IsNullOrEmpty( propValue ) )
                        writer.WriteAttributeString( "xmlName", propValue );
                }
            }
            // IsReadOnly
            if( !serializationContext.Result.Failed )
            {
                global::System.Boolean propValue = instanceOfConfigurationProperty.IsReadOnly;
                string serializedPropValue = SerializationUtilities.GetString<global::System.Boolean>( serializationContext, propValue );
                if( !serializationContext.Result.Failed )
                {
                    if( serializationContext.WriteOptionalPropertiesWithDefaultValue || string.CompareOrdinal( serializedPropValue, "False" ) != 0 )
                    {	// No need to write the value out if it's the same as default value.
                        writer.WriteAttributeString( "isReadOnly", serializedPropValue );
                    }
                }
            }
            // Documentation
            if( !serializationContext.Result.Failed )
            {
                global::System.String propValue = instanceOfConfigurationProperty.Documentation;
                if( !serializationContext.Result.Failed )
                {
                    if( !string.IsNullOrEmpty( propValue ) )
                        writer.WriteAttributeString( "documentation", propValue );
                }
            }
            // CustomAttributes
            /*if( !serializationContext.Result.Failed )
            {
                AttributeList propValue = instanceOfConfigurationProperty.CustomAttributes;
                string serializedPropValue = SerializationUtilities.GetString<AttributeList>( serializationContext, propValue );
                if( !serializationContext.Result.Failed )
                {
                    writer.WriteAttributeString( "customAttributes", serializedPropValue );
                }
            }*/
            // Browsable
            if( !serializationContext.Result.Failed )
            {
                global::System.Boolean propValue = instanceOfConfigurationProperty.Browsable;
                string serializedPropValue = SerializationUtilities.GetString<global::System.Boolean>( serializationContext, propValue );
                if( !serializationContext.Result.Failed )
                {
                    if( serializationContext.WriteOptionalPropertiesWithDefaultValue || string.CompareOrdinal( serializedPropValue, "true" ) != 0 )
                    {	// No need to write the value out if it's the same as default value.
                        writer.WriteAttributeString( "browsable", serializedPropValue );
                    }
                }
            }
            // DisplayName
            if( !serializationContext.Result.Failed )
            {
                global::System.String propValue = instanceOfConfigurationProperty.DisplayName;
                if( !serializationContext.Result.Failed )
                {
                    if( !string.IsNullOrEmpty( propValue ) )
                        writer.WriteAttributeString( "displayName", propValue );
                }
            }
            // Category
            if( !serializationContext.Result.Failed )
            {
                global::System.String propValue = instanceOfConfigurationProperty.Category;
                if( !serializationContext.Result.Failed )
                {
                    if( !string.IsNullOrEmpty( propValue ) )
                        writer.WriteAttributeString( "category", propValue );
                }
            }
        }

        /// <summary>
        /// Custom deserialization of ConfigurationProperty's elements. This is required because we're
        /// storing the CustomAttributes as elements.
        /// </summary>
        private void CustomReadElements( SerializationContext serializationContext, ModelElement element, XmlReader reader )
        {
            ConfigurationProperty property = (ConfigurationProperty)element;

            if( reader.Name != "customAttributes" )
            {
                ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.UnexpectedXmlElement( serializationContext, reader );
                return;
            }

            XmlReader caReader = reader.ReadSubtree();
            AttributeList attributeList = new AttributeList();
            while( caReader.Read() )
            {
                switch( caReader.NodeType )
                {
                    case XmlNodeType.Element:
                        if( caReader.Name == "customAttributes" )
                            continue;

                        if( caReader.Name != "attribute" )
                        {
                            ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.UnexpectedXmlElement( serializationContext, caReader );
                            continue;
                        }

                        Attribute attribute = new Attribute();
                        attributeList.AddLast( attribute );

                        while( caReader.MoveToNextAttribute() )
                        {
                            switch( caReader.Name )
                            {
                                case "name":
                                    attribute.Name = caReader.Value;
                                    break;

                                default:
                                    ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.IgnoredPropertyValue( serializationContext, caReader, caReader.Name, caReader.ValueType, caReader.Value );
                                    break;
                            }
                        }

                        if( string.IsNullOrEmpty( attribute.Name.Trim() ) )
                            attribute.Name = "INVALID";

                        caReader.Read();
                        while( caReader.NodeType != XmlNodeType.Element )
                            caReader.Read();

                        if( caReader.Name != "parameters" )
                        {
                            ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.UnexpectedXmlElement( serializationContext, reader );
                            continue;
                        }

                        XmlReader paReader = caReader.ReadSubtree();
                        while( paReader.Read() )
                        {
                            switch( paReader.NodeType )
                            {
                                case XmlNodeType.Element:
                                    if( paReader.Name == "parameters" ) continue;

                                    if( paReader.Name != "parameter" )
                                    {
                                        ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.UnexpectedXmlElement( serializationContext, paReader );
                                        continue;
                                    }

                                    AttributeParameter parameter = new AttributeParameter();
                                    attribute.Parameters.AddLast( parameter );
                                    while( paReader.MoveToNextAttribute() )
                                    {
                                        switch( caReader.Name )
                                        {
                                            case "name":
                                                parameter.Name = paReader.Value;
                                                break;

                                            case "value":
                                                parameter.Value = paReader.Value;
                                                break;

                                            default:
                                                ConfigurationSectionDesignerSerializationBehaviorSerializationMessages.IgnoredPropertyValue( serializationContext, paReader, paReader.Name, paReader.ValueType, paReader.Value );
                                                break;
                                        }
                                    }

                                    if( string.IsNullOrEmpty( parameter.Value.Trim() ) )
                                        parameter.Value = "INVALID";
                                    break;
                            }
                        }
                        break;
                }
            }
            property.CustomAttributes = attributeList;

            reader.Skip();
            while( reader.NodeType != XmlNodeType.Element )
                reader.Skip();
        }

        /// <summary>
        /// Custom serialization of ConfigurationProperty's elements. This is required because we're
        /// storing the CustomAttributes as elements.
        /// </summary>
        private void CustomWriteElements( SerializationContext serializationContext, ModelElement element, XmlWriter writer )
        {
            ConfigurationProperty property = (ConfigurationProperty)element;

            writer.WriteStartElement( "customAttributes" );
            foreach( Attribute attribute in property.CustomAttributes )
            {
                writer.WriteStartElement( "attribute" );
                writer.WriteStartAttribute( "name" );
                writer.WriteString( attribute.Name );
                writer.WriteEndAttribute();

                writer.WriteStartElement( "parameters" );
                foreach( AttributeParameter parameter in attribute.Parameters )
                {
                    writer.WriteStartElement( "parameter" );
                    if( !string.IsNullOrEmpty( parameter.Name ) )
                    {
                        writer.WriteStartAttribute( "name" );
                        writer.WriteString( parameter.Name );
                        writer.WriteEndAttribute();
                    }

                    writer.WriteStartAttribute( "value" );
                    writer.WriteString( parameter.Value );
                    writer.WriteEndAttribute();

                    writer.WriteEndElement();
                }

                writer.WriteEndElement();

                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }

        #region Use default implementations
        private void CustomConstructor()
        {
            DefaultConstructor();
        }

        private ModelElement CustomTryCreateInstance( SerializationContext serializationContext, XmlReader reader, Partition partition )
        {
            return DefaultTryCreateInstance( serializationContext, reader, partition );
        }

        private ModelElement CustomCreateInstance( SerializationContext serializationContext, XmlReader reader, Partition partition )
        {
            return DefaultCreateInstance( serializationContext, reader, partition );
        }

        private Moniker CustomTryCreateMonikerInstance( SerializationContext serializationContext, XmlReader reader, ModelElement sourceRolePlayer, Guid relDomainClassId, Partition partition )
        {
            return DefaultTryCreateMonikerInstance( serializationContext, reader, sourceRolePlayer, relDomainClassId, partition );
        }

        private Moniker CustomCreateMonikerInstance( SerializationContext serializationContext, XmlReader reader, ModelElement sourceRolePlayer, Guid relDomainClassId, Partition partition )
        {
            return DefaultCreateMonikerInstance( serializationContext, reader, sourceRolePlayer, relDomainClassId, partition );
        }

        private string CustomCalculateQualifiedName( DomainXmlSerializerDirectory directory, ModelElement element )
        {
            return DefaultCalculateQualifiedName( directory, element );
        }

        private string CustomGetMonikerQualifier( DomainXmlSerializerDirectory directory, ModelElement element )
        {
            return DefaultGetMonikerQualifier( directory, element );
        }
        #endregion
    }
}
